#include <thread>
#include <glog/logging.h>
#include <glog/log_severity.h>

#include "kcp_agent.h"
#include "utils.h"
#include "incoming_handle.h"

KcpAgent::KcpAgent() : kcp_(NULL), sockfd_(-1), processing_(false)
{
	memset(&client_addr_, 0, sizeof(struct sockaddr_in));
	memset(buf_, 0, sizeof(buf_));

	num_ = 0;
}

KcpAgent::~KcpAgent()
{
	LOG(INFO)<<"KcpAgent::~KcpAgent";
	
	if (kcp_)
	{
		ikcp_release(kcp_);
	}
}

void KcpAgent::Handle()
{
	while(1)
	{
		std::unique_lock<std::mutex> lck(queue_mtx_);

		processing_ = false;

		while(buf_q_.empty())
			queue_cv_.wait(lck);

		processing_ = true;

		PktData data = buf_q_.front();

		buf_q_.pop();

		lck.unlock();

		std::unique_lock<std::mutex> kcp_lck(kcp_mtx_);

		int ret = ikcp_input(kcp_, (const char *)data.data_, data.size_);

		bool has_data = false;
		int got = 0;

		while(1)	
		{			
			ret = ikcp_recv(kcp_, (char *)data.data_, data.size_);
			if (ret > 0)
			{
				has_data = true;
				got = ret;
			}
			
			if(ret < 0)
				break;
		}

		if (true == has_data)
		{		
			LOG(INFO)<<"client info, ip: "<<inet_ntoa(client_addr_.sin_addr)<<" port: "<<ntohs(client_addr_.sin_port)<<" data: "<<(char *)data.data_;

			if (strncmp((char *)data.data_, "OK", 2))
			{
				ikcp_send(kcp_, "OK", 3);
				ikcp_update(kcp_, iclock());

				num_++;
				
				LOG(INFO)<<"Send "<<num_<<" times";

				SignalCmd *signal_cmd = new SignalCmd(0);
				signal_cmd->priv_ = this;
				signal_cmd->size_ = got;
				memcpy(signal_cmd->payload_, data.data_, got);

				MessageDispatcher::Instance()->DispatchMsg(signal_cmd);
			}
		}

		//kcp_lck.unlock();
	}
}

void KcpAgent::Refresh()
{
	while(1)
	{
		usleep(10*1000);
		this->Update(iclock());
	}
}

void KcpAgent::SetClientAddr(struct sockaddr_in *addr)
{
	memcpy(&this->client_addr_, addr, sizeof(struct sockaddr_in));
}

void KcpAgent::AllocKcp(IUINT32 conv)
{
	kcp_ = ikcp_create(conv, this);
	kcp_->output = OutputProc;
	ikcp_nodelay(kcp_, 0, 10, 0, 0);//1, 10, 2, 1
	ikcp_wndsize(kcp_, 128, 128);

	LOG(INFO)<<"AllocKcp,  conv = "<<kcp_->conv<<" kcp: "<<kcp_;
}

void KcpAgent::Feed(PktData data)
{
	std::unique_lock<std::mutex> lck(queue_mtx_);

	buf_q_.emplace(data);

	lck.unlock();

	queue_cv_.notify_one(); 
}

void KcpAgent::SetFd(int fd)
{
	sockfd_ = fd;
}

void KcpAgent::Start()
{
	std::thread t1(&KcpAgent::Handle, this);
	t1.detach();

	std::thread t2(&KcpAgent::Refresh, this);
	t2.detach();
}

void KcpAgent::Send(std::string &buf)
{
	std::unique_lock<std::mutex> lck(kcp_mtx_);
	
	ikcp_send(kcp_, buf.c_str(), buf.length());

	LOG(INFO)<<"KcpAgent::Send, buf="<<buf<<", len: "<<buf.length();
	
	ikcp_update(kcp_, iclock());

	//lck.unlock();
}

void KcpAgent::Update(IUINT32 current)
{
	std::unique_lock<std::mutex> lck(kcp_mtx_);
	
	ikcp_update(kcp_, current);

	//lck.unlock();
}

int KcpAgent::OutputProc(const char *buf, int len, struct IKCPCB *kcp, void *user)
{
	KcpAgent *kcp_agent = (KcpAgent *)user;

	int n = sendto(kcp_agent->sockfd_, buf, len, 0, (struct sockaddr *)&kcp_agent->client_addr_, sizeof(struct sockaddr_in));
	if (n >= 0) 	  
	{	
		if (n >= 48)
		{
			LOG(INFO)<<"++++OutputProc-send: bytes: "<<n<<", buf: "<<(char *)(buf+24+24);
		}
		else if (n >= 24)
		{
			LOG(INFO)<<"++++OutputProc-send: bytes: "<<n<<", buf: "<<buf+24;
		}

		return n;
	} 
	else 
	{
		LOG(INFO)<<"OutputProc error, send "<<n<<" bytes";
		
		return -1;
	}
}

